package main

import (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"gitee.com/jn-qq/go-tools/slices"
	"log"
	"os"
	"sort"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/go-redis/redis/v8"
)

// Config 配置结构体
type Config struct {
	ConnectionMode string   // 连接模式: cluster, sentinel, standalone
	Addr           string   // 主机/节点 地址，用, 分隔
	Password       string   // 密码
	MasterName     string   // 哨兵模式下的主节点名称
	DB             int      // 单节点数据库编号
	ScanMatch      string   // 扫描匹配模式
	DeleteNoTTL    bool     // 是否删除无TTL的key
	WhiteList      []string // 白名单前缀
	BatchSize      int      // 扫描批次大小
	Concurrency    int      // 并发数
	Depth          int      // 分析层级深度
	TopN           int      // 输出前N个统计结果
	Round          int      // 跟BatchSize搭配控制数量，默认-1，全部执行
	Separator      string   // key分隔符
	AllDelete      bool
}

// KeyStats Key统计信息
type KeyStats struct {
	Count  int64 `json:"count"`
	Memory int64 `json:"memory"`
}

// KeyAnalysisResult 分析结果
type KeyAnalysisResult struct {
	TotalKeys       int64               `json:"total_keys"`
	NoTTLKeys       int64               `json:"no_ttl_keys"`
	DeletedKeys     int64               `json:"deleted_keys"`
	LevelStats      map[string]KeyStats `json:"level_stats"`
	NoTTLLevelStats map[string]KeyStats `json:"no_ttl_level_stats"`
	StartTime       time.Time           `json:"start_time"`
	EndTime         time.Time           `json:"end_time"`
	Duration        time.Duration       `json:"duration"`
}

// RedisManager Redis管理器
type RedisManager struct {
	client redis.UniversalClient
	config Config
}

// main 程序入口
func main() {
	// 解析命令行参数
	config := parseFlags()

	// 创建Redis客户端
	client, err := newRedisClient(config)
	if err != nil {
		log.Fatalf("Failed to create Redis client: %v", err)
	}
	defer client.Close()

	// 创建Redis管理器
	rm := &RedisManager{
		client: client,
		config: config,
	}

	ctx := context.Background()

	fmt.Printf("Starting Redis key analysis (mode: %s)...\n", config.ConnectionMode)

	// 流式处理keys
	result, err := rm.streamProcessKeys(ctx)
	if err != nil {
		log.Fatalf("Failed to process keys: %v", err)
	}

	// 打印报告
	rm.printReport(result)

	// 导出JSON报告
	reportFile := fmt.Sprintf("redis_analysis_report_%s.json",
		time.Now().Format("20060102_150405"))
	if err := rm.exportReport(result, reportFile); err != nil {
		log.Printf("Failed to export report: %v", err)
	} else {
		fmt.Printf("Report exported to: %s\n", reportFile)
	}
}

// parseFlags 解析命令行参数
func parseFlags() Config {
	var config Config

	flag.StringVar(&config.ConnectionMode, "mode", "standalone", "连接模式: cluster, sentinel, standalone")
	flag.StringVar(&config.Addr, "addr", "localhost:6379", "Redis主机/节点地址")
	flag.StringVar(&config.Password, "password", "", "Redis密码")
	flag.StringVar(&config.MasterName, "master", "mymaster", "哨兵模式主节点名称")
	flag.IntVar(&config.DB, "db", 0, "单节点数据库编号")
	flag.StringVar(&config.ScanMatch, "match", "*", "扫描匹配模式")
	flag.BoolVar(&config.DeleteNoTTL, "delete", false, "是否删除无TTL的key")
	flag.IntVar(&config.BatchSize, "batch", 1000, "扫描批次大小")
	flag.IntVar(&config.Concurrency, "concurrency", 10, "并发数")
	flag.IntVar(&config.Depth, "depth", 3, "分析层级深度")
	flag.IntVar(&config.TopN, "top", 20, "输出前N个统计结果")
	flag.IntVar(&config.Round, "round", -1, "扫描轮数，-1表示全部扫描")
	flag.StringVar(&config.Separator, "separator", ":", "key分隔符")
	flag.BoolVar(&config.AllDelete, "ad", false, "直接删除")

	// 白名单可以通过多次指定来添加多个
	var whiteList string
	flag.StringVar(&whiteList, "whitelist", "", "白名单前缀(多个用逗号分隔)")

	flag.Parse()

	if whiteList != "" {
		config.WhiteList = strings.Split(whiteList, ",")
		for i := range config.WhiteList {
			config.WhiteList[i] = strings.TrimSpace(config.WhiteList[i])
		}
	}

	// 参数校验
	if !slices.Contains([]string{"cluster", "sentinel", "standalone"}, config.ConnectionMode) {
		log.Fatalf("Invalid connection mode: %s, you can select cluster、sentinel、standalone", config.ConnectionMode)
	}
	if config.Addr == "" {
		log.Fatalf("Redis地址不能为空")
	}
	if config.DB < 0 || config.DB > 15 {
		log.Fatalf("数据库编号不合理: %d", config.DB)
	}
	if config.ScanMatch == "" {
		log.Fatalf("扫描匹配模式不能为空")
	}
	if config.BatchSize <= 0 {
		log.Fatalf("批次大小必须大于0")
	}
	if config.Concurrency <= 0 {
		log.Fatalf("并发数必须大于0")
	}
	if config.Depth <= 0 {
		log.Fatalf("分析层级深度必须大于0")
	}
	if config.TopN <= 0 {
		log.Fatalf("输出前N个统计结果必须大于0")
	}

	// 校验分隔符不为空
	if config.Separator == "" {
		log.Fatalf("key分隔符不能为空")
	}

	return config
}

// newRedisClient 创建Redis客户端
func newRedisClient(config Config) (redis.UniversalClient, error) {
	ctx := context.Background()

	switch config.ConnectionMode {
	case "cluster":
		client := redis.NewClusterClient(&redis.ClusterOptions{
			Addrs:    strings.Split(config.Addr, ","),
			Password: config.Password,
		})

		// 测试连接
		if err := client.Ping(ctx).Err(); err != nil {
			return nil, fmt.Errorf("cluster connection failed: %v", err)
		}
		return client, nil

	case "sentinel":
		client := redis.NewFailoverClient(&redis.FailoverOptions{
			MasterName:    config.MasterName,
			SentinelAddrs: strings.Split(config.Addr, ","),
			Password:      config.Password,
			DB:            config.DB,
		})

		if err := client.Ping(ctx).Err(); err != nil {
			return nil, fmt.Errorf("sentinel connection failed: %v", err)
		}
		return client, nil

	case "standalone":
		client := redis.NewClient(&redis.Options{
			Addr:     config.Addr,
			Password: config.Password,
			DB:       config.DB,
		})

		if err := client.Ping(ctx).Err(); err != nil {
			return nil, fmt.Errorf("standalone connection failed: %v", err)
		}
		return client, nil

	default:
		return nil, fmt.Errorf("unsupported connection mode: %s", config.ConnectionMode)
	}
}

// streamProcessKeys 流式处理keys
func (rm *RedisManager) streamProcessKeys(ctx context.Context) (*KeyAnalysisResult, error) {
	result := &KeyAnalysisResult{
		LevelStats:      make(map[string]KeyStats),
		NoTTLLevelStats: make(map[string]KeyStats),
		StartTime:       time.Now(),
	}

	// 使用原子操作的计数器
	var totalKeys, noTTLKeys, deletedKeys int64

	// 使用互斥锁保护共享数据
	var mu sync.Mutex

	// 创建工作池
	workChan := make(chan string, rm.config.BatchSize*2)

	// 启动工作goroutine
	var wg sync.WaitGroup
	for i := 0; i < rm.config.Concurrency; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			rm.worker(ctx, workChan, result, &mu, &noTTLKeys, &deletedKeys)
		}()
	}

	// 扫描并发送keys到工作池
	go func() {
		defer close(workChan)

		var round int
		var cursor uint64
		for {
			select {
			case <-ctx.Done():
				return
			default:
			}
			keys, nextCursor, err := rm.client.Scan(ctx, cursor, rm.config.ScanMatch, int64(rm.config.BatchSize)).Result()
			if err != nil {
				log.Printf("Scan error: %v", err)
				return
			}

			atomic.AddInt64(&totalKeys, int64(len(keys)))

			for _, key := range keys {
				select {
				case workChan <- key:
				case <-ctx.Done():
					return
				}
			}

			if nextCursor == 0 {
				break
			}
			cursor = nextCursor

			round += 1

			if rm.config.Round > 0 && round >= rm.config.Round {
				break
			}
		}
	}()

	// 等待所有工作完成
	wg.Wait()

	// 设置最终结果
	result.TotalKeys = atomic.LoadInt64(&totalKeys)
	result.NoTTLKeys = atomic.LoadInt64(&noTTLKeys)
	result.DeletedKeys = atomic.LoadInt64(&deletedKeys)
	result.EndTime = time.Now()
	result.Duration = result.EndTime.Sub(result.StartTime)

	return result, nil
}

// worker 工作goroutine
func (rm *RedisManager) worker(
	ctx context.Context,
	workChan <-chan string,
	result *KeyAnalysisResult,
	mu *sync.Mutex,
	noTTLKeys, deletedKeys *int64,
) {
	// 用于收集需要删除的key的批次
	deleteBatch := make([]string, 0, 1000)

	for key := range workChan {
		// 获取TTL
		ttl, err := rm.client.TTL(ctx, key).Result()
		if err != nil {
			log.Printf("Failed to get TTL for key %s: %v", key, err)
			continue
		}

		// 获取内存使用量
		memory, err := rm.client.MemoryUsage(ctx, key).Result()
		if err != nil {
			// 某些key可能不支持MEMORY USAGE命令
			memory = 0
		}

		// 判断是否需要删除
		isNoTTL := ttl == -1
		shouldDelete := isNoTTL && rm.config.DeleteNoTTL && !rm.isWhiteListed(key)

		if shouldDelete || rm.config.AllDelete {
			// 添加到删除批次中
			deleteBatch = append(deleteBatch, key)

			// 当批次达到配置大小时，执行批量删除
			if len(deleteBatch) >= 1000 {
				rm.batchDelete(ctx, deleteBatch, deletedKeys)
				deleteBatch = deleteBatch[:0] // 重置批次
			}
		}

		if isNoTTL {
			atomic.AddInt64(noTTLKeys, 1)
		}

		// 获取层级前缀
		level := rm.getLevelPrefix(key)

		// 更新统计信息
		mu.Lock()
		if existing, exists := result.LevelStats[level]; exists {
			result.LevelStats[level] = KeyStats{
				Count:  existing.Count + 1,
				Memory: existing.Memory + memory,
			}
		} else {
			result.LevelStats[level] = KeyStats{Count: 1, Memory: memory}
		}
		// 如果key没有TTL，则也更新NoTTLLevelStats统计信息
		if isNoTTL {
			if existing, exists := result.NoTTLLevelStats[level]; exists {
				result.NoTTLLevelStats[level] = KeyStats{
					Count:  existing.Count + 1,
					Memory: existing.Memory + memory,
				}
			} else {
				result.NoTTLLevelStats[level] = KeyStats{Count: 1, Memory: memory}
			}
		}
		mu.Unlock()
	}

	// 处理剩余的删除批次
	if len(deleteBatch) > 0 {
		rm.batchDelete(ctx, deleteBatch, deletedKeys)
	}
}

// isWhiteListed 检查key是否在白名单中
func (rm *RedisManager) isWhiteListed(key string) bool {
	for _, prefix := range rm.config.WhiteList {
		if strings.HasPrefix(key, prefix) {
			return true
		}
	}
	return false
}

// getLevelPrefix 获取指定层级的前缀
func (rm *RedisManager) getLevelPrefix(key string) string {
	parts := strings.Split(key, rm.config.Separator)
	if len(parts) <= rm.config.Depth {
		return key
	}
	return strings.Join(parts[:rm.config.Depth], rm.config.Separator)
}

// printReport 打印报告
func (rm *RedisManager) printReport(result *KeyAnalysisResult) {
	fmt.Println("=================== Redis Key Analysis Report ===================")
	fmt.Printf("Start Time: %s\n", result.StartTime.Format("2006-01-02 15:04:05"))
	fmt.Printf("End Time: %s\n", result.EndTime.Format("2006-01-02 15:04:05"))
	fmt.Printf("Duration: %v\n", result.Duration)
	fmt.Println()

	fmt.Printf("Total Keys: %d\n", result.TotalKeys)
	fmt.Printf("Keys without TTL: %d (%.2f%%)\n", result.NoTTLKeys,
		float64(result.NoTTLKeys)/float64(result.TotalKeys)*100)
	fmt.Printf("Deleted Keys: %d\n", result.DeletedKeys)
	fmt.Println()

	// 输出没有TTL的key的层级统计
	rm.printNoTTLLevelStats(result)
	fmt.Println()

	// 转换为切片进行排序
	type LevelStat struct {
		Level string
		Stats KeyStats
	}

	var levelStats []LevelStat
	for level, stats := range result.LevelStats {
		levelStats = append(levelStats, LevelStat{Level: level, Stats: stats})
	}

	// 按数量排序
	sort.Slice(levelStats, func(i, j int) bool {
		return levelStats[i].Stats.Count > levelStats[j].Stats.Count
	})

	fmt.Printf("Top %d Key Prefixes by Count:\n", rm.config.TopN)
	fmt.Println("Rank\tPrefix\t\t\t\tCount\t\tMemory(MB)")
	fmt.Println(strings.Repeat("-", 80))

	for i, stat := range levelStats {
		if i >= rm.config.TopN {
			break
		}
		memoryMB := float64(stat.Stats.Memory) / 1024 / 1024
		fmt.Printf("%d\t%-30s\t%d\t\t%.2f\n",
			i+1, stat.Level, stat.Stats.Count, memoryMB)
	}

	fmt.Println()

	// 按内存使用量排序
	sort.Slice(levelStats, func(i, j int) bool {
		return levelStats[i].Stats.Memory > levelStats[j].Stats.Memory
	})

	fmt.Printf("Top %d Key Prefixes by Memory Usage:\n", rm.config.TopN)
	fmt.Println("Rank\tPrefix\t\t\t\tMemory(MB)\tCount")
	fmt.Println(strings.Repeat("-", 80))

	for i, stat := range levelStats {
		if i >= rm.config.TopN {
			break
		}
		memoryMB := float64(stat.Stats.Memory) / 1024 / 1024
		fmt.Printf("%d\t%-30s\t%.2f\t\t%d\n",
			i+1, stat.Level, memoryMB, stat.Stats.Count)
	}

	fmt.Println("=================================================================")
}

// printNoTTLLevelStats 打印没有TTL的key的层级统计
func (rm *RedisManager) printNoTTLLevelStats(result *KeyAnalysisResult) {
	// 转换为切片进行排序
	type LevelStat struct {
		Level string
		Stats KeyStats
	}

	var levelStats []LevelStat
	for level, stats := range result.NoTTLLevelStats {
		levelStats = append(levelStats, LevelStat{Level: level, Stats: stats})
	}

	if len(levelStats) == 0 {
		fmt.Println("No keys without TTL found.")
		return
	}

	// 按数量排序
	sort.Slice(levelStats, func(i, j int) bool {
		return levelStats[i].Stats.Count > levelStats[j].Stats.Count
	})

	fmt.Printf("Top %d Key Prefixes by Count (No TTL):\n", rm.config.TopN)
	fmt.Println("Rank\tPrefix\t\t\t\tCount\t\tMemory(MB)")
	fmt.Println(strings.Repeat("-", 80))

	for i, stat := range levelStats {
		if i >= rm.config.TopN {
			break
		}
		memoryMB := float64(stat.Stats.Memory) / 1024 / 1024
		fmt.Printf("%d\t%-30s\t%d\t\t%.2f\n",
			i+1, stat.Level, stat.Stats.Count, memoryMB)
	}

	fmt.Println()

	// 按内存使用量排序
	sort.Slice(levelStats, func(i, j int) bool {
		return levelStats[i].Stats.Memory > levelStats[j].Stats.Memory
	})

	fmt.Printf("Top %d Key Prefixes by Memory Usage (No TTL):\n", rm.config.TopN)
	fmt.Println("Rank\tPrefix\t\t\t\tMemory(MB)\tCount")
	fmt.Println(strings.Repeat("-", 80))

	for i, stat := range levelStats {
		if i >= rm.config.TopN {
			break
		}
		memoryMB := float64(stat.Stats.Memory) / 1024 / 1024
		fmt.Printf("%d\t%-30s\t%.2f\t\t%d\n",
			i+1, stat.Level, memoryMB, stat.Stats.Count)
	}
}

// exportReport 导出报告到JSON文件
func (rm *RedisManager) exportReport(result *KeyAnalysisResult, filename string) error {
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	encoder := json.NewEncoder(file)
	encoder.SetIndent("", "  ")
	return encoder.Encode(result)
}

// batchDelete 批量删除key
func (rm *RedisManager) batchDelete(ctx context.Context, keys []string, deletedKeys *int64) {
	if len(keys) == 0 {
		return
	}

	// 执行批量删除
	result, err := rm.client.Unlink(ctx, keys...).Result()
	if err != nil {
		log.Printf("Failed to delete batch of keys: %v", err)
		return
	}

	// 更新删除计数器
	atomic.AddInt64(deletedKeys, result)
}
